package graphic.complex;

import graphic.primitives.general.Background;
import graphic.primitives.general.circlesegment.CircleSegmentRightUp;
import graphic.primitives.general.IGraphicObject;
import graphic.primitives.general.circlesegment.CircleSegmentUp;
import graphic.primitives.general.GraphicObject;
import graphic.primitives.car.DoorHandle;
import graphic.primitives.car.BackLight;
import graphic.primitives.car.BackPart;
import graphic.primitives.car.BackWindow;
import graphic.primitives.car.Chassis;
import graphic.primitives.car.Doors;
import graphic.primitives.car.Exhaust;
import graphic.primitives.car.FrontLight;
import graphic.primitives.car.FrontPart;
import graphic.primitives.car.FrontWindow;
import graphic.primitives.car.Roof;
import graphic.primitives.car.Smoke;
import graphic.primitives.car.Wheel;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 * Class for 2D car.
 * 
 * @author Vit Terl, Tomas Zima
 */
public class Car extends GraphicObject implements IGraphicObject
{
	/**
	 * Constructor.
	 * 
	 * @param void
	 */
	public Car(int size)
	{
		// List for all graphic objects
		objects = new LinkedList<GraphicObject>();
		
		// Set default position and size of car
		setXY(0, 0);
		setCarSize(size);
		setDirection(0);
		
		// Alias
		int _x = currentX;
		int _y = currentY;

		// Resize ratio
		float resizeRatio	= ((float) carSize / 265.0f);
		float rR			= resizeRatio;					// Alias
	
		// Coeficients
		int k[][]			= {
								{(int) (40 * rR), (int) (30 * rR), (int) (55 * rR)},
								{(int) (55 * rR), (int) (15 * rR)},
								{(int) (55 * rR), (int) (30 * rR), (int) (75 * rR), (int) (25 * rR), (int) (150 * rR)},
								{(int) (265 * rR), (int) (45 * rR), (int) (30 * rR), (int) (60 * rR)},
								{(int) (205 * rR), (int) (15 * rR)},
								{(int) (70 * rR), (int) (120 * rR)},
								{(int) (165 * rR), (int) (30 * rR), (int) (40 * rR), (int) (25 * rR)},
								{(int) (95 * rR), (int) (30 * rR), (int) (40 * rR), (int) (25 * rR)},
								{(int) (55 * rR), (int) (20 * rR)},
								{(int) (205 * rR), (int) (20 * rR)},
								{(int) (95 * rR), (int) (5 * rR), (int) (35 * rR), (int) (50 * rR), (int) (25 * rR)},
								{(int) (130 * rR), (int) (5 * rR), (int) (35 * rR), (int) (50 * rR), (int) (25 * rR)},
								{(int) (135 * rR), (int) (-28 * rR), (int) (50 * rR)},
								{(int) (100 * rR), (int) (-28 * rR), (int) (50 * rR)},
								{(int) (5 * rR), (int) (20 * rR), (int) (5 * rR), (int) (10 * rR)},
								{(int) (265 * rR), (int) (22 * rR), (int) (13 * rR)},
								{(int) (10 * rR), (int) (5 * rR), (int) (10 * rR), (int) (5 * rR)},
								{(int) (140 * rR), (int) (55 * rR), (int) (15 * rR)},
								{(int) (265 * rR), (int) (30 * rR)}
							};
		
		// Objects
		BackPart a				= 		new BackPart(_x, _y, k[0][0], k[0][1], k[0][2]);
		CircleSegmentUp b		= 		new CircleSegmentUp(_x + k[1][0], _y, k[1][1]);
		Roof c					= 		new Roof(_x + k[2][0], _y - k[2][1], k[2][2], k[2][3], k[2][4]);
		FrontPart d				= 		new FrontPart(_x + k[3][0], _y, k[3][1], k[3][2], k[3][3]);
		CircleSegmentUp e		= 		new CircleSegmentUp(_x + k[4][0], _y, k[4][1]);
		Chassis f				= 		new Chassis(_x + k[5][0], _y, k[5][1]);
		FrontWindow i			= 		new FrontWindow(_x + k[6][0], _y - k[6][1], k[6][2], k[6][3]);
		BackWindow j			= 		new BackWindow(_x + k[7][0], _y - k[7][1], k[7][2], k[7][3]);
		Doors l					= 		new Doors(_x + k[10][0], _y - k[10][1], k[10][2], k[10][3], k[10][4]);
		Doors m					= 		new Doors(_x + k[11][0], _y - k[11][1], k[11][2], k[11][3], k[11][4]);
		BackLight p				= 		new BackLight(_x - k[14][0], _y - k[14][1], k[14][2], k[14][3]);
		FrontLight q			= 		new FrontLight(_x + k[15][0], _y - k[15][1], k[15][2]);
		Exhaust s				= 		new Exhaust(_x - k[16][0], _y - k[16][1], k[16][2], k[16][3]);
		CircleSegmentRightUp t	=		new CircleSegmentRightUp(_x + k[17][0], _y - k[17][1], k[17][2]);

		// DODELAT!
		// Nastaveni pozice kliky
		DoorHandle n		= new DoorHandle(_x + k[12][0], _y + k[12][1], k[12][2]);
		DoorHandle o		= new DoorHandle(_x + k[13][0], _y + k[13][1], k[13][2]);
		
		Wheel g 			= new Wheel(_x + k[8][0], _y, k[8][1]);
		Wheel h				= new Wheel(_x + k[9][0], _y, k[9][1]);

		Smoke r				= new Smoke(_x, _y);
	
		// Save reference to some controllable objects
		frontLight			= q;
		backLight			= p;
		frontWheel			= g;
		backWheel			= h;
		smoke				= r;
		leftDoors			= l;
		rightDoors			= m;
		
		objects.add(new Background(_x, _y, k[18][0], k[18][1]));
		
		objects.add(a);
		objects.add(b);
		objects.add(c);
		objects.add(d);
		objects.add(e);
		objects.add(f);
		objects.add(g);
		objects.add(h);
		objects.add(i);
		objects.add(j);
		objects.add(l);
		objects.add(m);
		objects.add(n);
		objects.add(o);
		objects.add(p);
		objects.add(q);
		objects.add(r);
		objects.add(s);
		objects.add(t);
	}
	
	/**
	 * Constructor.
	 * 
	 * @param int [x]
	 * @param int [y]
	 * @param int [size]
	 */
	public Car(int x, int y, int size)
	{
		this(size);
		this.setXY(x, y);
	}
	
	/**
	 * Redraw car.
	 * 
	 * @param void
	 * @return void
	 */
	@Override
	public void redraw()
	{
		float originalRotation		= mainWindow.getRotationAngle();
		boolean wrapperState		= mainWindow.isWrapperEnabled();

		mainWindow.enableWrapper();
		mainWindow.setRotationAngle(direction);
		
		Iterator<GraphicObject> it = objects.iterator();
		
		while (it.hasNext())
		{
			GraphicObject obj = it.next();
			obj.redraw();
		}
		
		mainWindow.setRotationAngle(originalRotation);
		
		if (!wrapperState)
		{
			mainWindow.disableWrapper();
		}
	}
	
	/**
	 * Set coordinates of car.
	 * Point [_x; _y] is in the left down corner of car.
	 * 
	 * @param int [_x]
	 * @param int [_y]
	 * @return void
	 */
	@Override
	public final void setXY(int x, int y)
	{
		Iterator<GraphicObject> it = objects.iterator();

		while (it.hasNext())
		{
			GraphicObject obj = it.next();
			
			obj.setXY(obj.getX() - (currentX - x), obj.getY() - (currentY - y));
		}
	}
	
	/**
	 * Move car at X axis for specific count of pixels.
	 * 
	 * @param int [movement]
	 * Count of pixels for move.
	 * 
	 * @return void
	 */
	public void moveAtXAxis(int movement)
	{
		this.setXY(this.getX() + movement, this.getY());
	}
	
	/**
	 * Move car at Y axis for specific count of pixels.
	 * 
	 * @param int [movement]
	 * Count of pixels for move.
	 * 
	 * @return void
	 */
	public void moveAtYAxis(int movement)
	{
		this.setXY(this.getX(), this.getY() + movement);
	}
	
	/**
	 * Return position of car in X axis.
	 * 
	 * @param void
	 * @return void
	 */
	@Override
	public int getX()
	{
		return x;
	}
	
	/**
	 * Return position of car in Y axis.
	 * 
	 * @param void
	 * @return void
	 */
	@Override
	public int getY()
	{
		return y;
	}
	
	/**
	 * Set size (width) of car in pixels.
	 * 
	 * @param int [size]
	 * @return void
	 */
	public final void setCarSize(int size)
	{
		this.carSize = size;
	}
	
	/**
	 * Set direction of car.
	 * 
	 * @param float [direction]
	 * @return void
	 */
	public final void setDirection(float direction)
	{
		this.direction = direction;
	}

	/**
	 * Change direction of car.
	 * 
	 * @param boolean [way]
	 *	True: up, false: down
	 * 
	 * @return void
	 */
	public final void changeDirection(boolean way)
	{
		if (way)
		{
			direction += directionChangeJump;
		}
		else
		{
			direction -= directionChangeJump;
		}
	}
	
	/**
	 * Returns current direction angle.
	 * 
	 * @param void
	 * @return float
	 */
	public float getDirectionAngle()
	{
		return direction;
	}
	
	/**
	 * Returns current size of car.
	 * 
	 * @param void
	 * @return int
	 */
	public int getCarSize()
	{
		return carSize;
	}
	
	public void switchFrontLight()
	{
		frontLight.prepniSvetlo();
	}
	
	public void switchBackLight()
	{
		backLight.prepniSvetlo();
	}
	
	public boolean isFrontLightOn()
	{
		return frontLight.jeRozviceno();
	}
	
	public boolean isBackLightOn()
	{
		return backLight.jeRozviceno();
	}
	
	public void rotateWheel(int angle)
	{
		this.frontWheel.setRotationAngle(angle);
		this.backWheel.setRotationAngle(angle);
	}
	
	public void enableSmoke()
	{
		this.smoke.enable();
	}
	
	public void disableSmoke()
	{
		this.smoke.disable();
	}
	
	public void pullDownLeftWindow()
	{
		leftDoors.down();
	}
	
	public void pullDownRightWindow()
	{
		rightDoors.down();
	}
	
	public void pullUpLeftWindow()
	{
		leftDoors.up();
	}
	
	public void pullUpRightWindow()
	{
		rightDoors.up();
	}
	
	/**
	 * List of all components of car.
	 */
	private List<GraphicObject> objects;
	
	/**
	 * Position of car in X axis.
	 * This is left down corn of car.
	 */
	private int currentX;
	
	/**
	 * Position of car in Y axis.
	 * This is left down corn of car.
	 */
	private int currentY;
	
	/**
	 * Size (width) of car in pixels.
	 */
	private int carSize;
	
	/**
	 * Direction (angle) of car.
	 */
	private float direction;
	
	/**
	 * Default change in direction angle per one keypress.
	 */
	private final float directionChangeJump = 1.5f;
	
	private BackLight	backLight;
	private FrontLight	frontLight;
	private Wheel		frontWheel;
	private Wheel		backWheel;
	private Smoke		smoke;
	private Doors		leftDoors;
	private Doors		rightDoors;
}
